Convertir una aplicación React.js con Vite a una aplicación de escritorio.
Descripción
Hasta el momento, el caso de uso que provoca que una aplicación desarrollada con React.js se convierta en un ejecutable es que cliente no cuenta con servicio de internet en el lugar donde se va a utilizar la aplicación, por lo tanto, se le proporciona un ejecutable que permita correr la aplicación de forma nativa. Esto se logra a través de Electron.
Para este ejemplo, el proyecto cuenta con las siguientes dependencias:
"react": "^18.2.0",
"electron-is-dev": "^3.0.1",
"react-router-dom": "^6.21.3",
"cross-env": "^7.0.3",
"electron": "^33.2.0",
"electron-builder": "^25.1.8",
"wait-on": "^8.0.1"
Nota: El ejemplo es utilizando un proyecto creado con Vite Nota: El ejemplo fue desarrollado con una aplicación CMS y con una aplicación con Three.js
Paso 1: Instalaciones necesarias
Las siguientes son instalaciones necesarias para transformar el proyecto
npm i electron-is-dev
npm i -D concurrently cross-env electron electron-builder wait-on
Paso 2:
Crear el archivo de configuración de Electron.
El nombre del archivo es importante y comúnmente se llama main.js, debe de ir en la raíz del proyecto (afuera de src). En caso de ocupar otro nombre, se debe tomar en cuenta para pasos posteriores.
import { app, BrowserWindow } from "electron";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
let mainWindow;
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
function createWindow() {
// Detecta si estamos en modo desarrollo
const isDev = process.env.NODE_ENV === "development";
// Crea la ventana principal
mainWindow = new BrowserWindow({
width: 1200,
height: 1000,
title: "Bimbo MTY", // **Este nombre aparecerá en la barra de tareas**
autoHideMenuBar: true,
icon: join(app.getAppPath(), "public/icon.icns"), // **Imagen icono de la aplicación (es icns porque se desarrollo en mac, para windows es ico)**
webPreferences: {
preload: join(__dirname, "preload.js"), // Ruta al archivo preload.js (permite exportar funciones de Electron, por el momento no se tiene un uso)
},
});
// Maximiza la ventana al abrir
mainWindow.maximize();
// Carga la URL según el entorno
const url = isDev
? "http://localhost:5173" // Asegúrate de que el servidor de desarrollo esté activo
: `file://${join(__dirname, "dist", "index.html")}`; // Archivo generado por Vite en modo producción
mainWindow.loadURL(url);
// Limpia la referencia a la ventana cuando se cierra
mainWindow.on("closed", () => {
mainWindow = null;
});
}
// Espera a que la app esté lista para crear la ventana
app.whenReady().then(() => {
createWindow();
// En macOS, vuelve a abrir la ventana si no hay ninguna activa
app.on("activate", () => {
// En macOS, vuelve a abrir la ventana si todas están cerradas
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
// Salir de la aplicación cuando todas las ventanas están cerradas, excepto en macOS
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
Paso 3 (Opcional): Agregar el preload.js
Por el momento no se ha requerido el uso del archivo preload, pero es una buen práctica agregarlo con el objetivo de usar funciones de Electron. Debe de ir en la raíz del proyecto, mismo nivel que el archivo del paso
import { contextBridge } from "electron";
contextBridge.exposeInMainWorld("myApi", {
// Aquí puedes exponer funciones del lado de Electron al frontend
});
Paso 4: Crear carpeta de íconos
Dentro de la carpeta public
, estarán los íconos para los diferentes sistemas operativos:
- Para Windows: archivo
.ico
. - Para Mac: archivo
.icns
. - Para Linux: archivo
.png
.
Es importante que las imágenes tengan una resolución de 256 x 256 píxeles.
Paso 5: Modificar package.json
Agrega los siguientes scripts y configuraciones en tu archivo package.json
:
- Información del autor y descripción del proyecto:
"author": {
"name": "Inmersys web developer",
"email": "inmersys.developer@gmail.com"
},
"description": "CMS for VRTraining created with React 18 and built with Electron",
"homepage": "./",
- Agregar el archivo de configuración de Electron. El nombre del archivo propuesto como main.js debe de concidir en este paso
"main": "main.js",
- Comandos para ejecutar el proyecto en modo desarrollo y producción:
"scripts": {
"electron:dev": "cross-env NODE_ENV=development concurrently \"vite\" \"wait-on http://localhost:5173 && electron .\"", // En desarrollo se puede apreciar una versión cercana a la final.
"electron:build": "vite build && electron-builder",
"electron:build:mac": "vite build && electron-builder --mac", // Únicamente genera el ejecutable para mac
"electron:build:win": "vite build && electron-builder --win"// Únicamente genera el ejecutable para windows
}
- Configuración de los archivos de producción
"build": {
"productName": "Bimbo MTY",
"copyright": "Copyright © 2022 Inmersys",
"electronVersion": "33.2.0",
"appId": "com.bimbo.electron",
"directories": {
"buildResources": "public",
"output": "dist_electron" // Carpeta que se generará al finalizar el compilado de Electron
},
"files": [
"dist/**/*", // Este es el único necesario, ya que antes del compilado de electron, se crea el build/dist de react
"main.js",
"preload.js"
],
"win": {
"target": "nsis",
"icon": "public/logo_marinela.png"
},
"mac": {
"target": "dmg",
"icon": "public/icon.icns",
"identity": null
},
"linux": {
"icon": "public/icon.png",
"target": "deb"
}
}
Paso 8: Modificar rutas en el proyecto
Cambia BrowserRouter
por HashRouter
en las rutas de tu proyecto provenientes de la librería react-router-dom
. Si se esta usando v6
o superior se debe modificar la forma de agregar hijos con una configuración simialr a la v5
.
import { HashRouter } from "react-router-dom";
<HashRouter>
<main className="fixed top-0 w-full h-full font-montserrat">
{/* Aquí define tus rutas */}
<Routes>
{/* Rutas privadas */}
<Route path={routerPrivate.path} element={routerPrivate.element}>
<Route index element={routerPrivate.children[0].element} />
</Route>
{/* Ruta de error */}
<Route path="*" element={<NotFound />} />
</Routes>
<Versioner />
</main>
</HashRouter>
Paso 9: Generar instalables
Para obtener los instalables que usaremos en nuestros diferentes sistemas operativos, ejecuta uno de los siguientes comandos:
npm run electron:build:mac
npm run electron:build:win
Paso 10: Ubicar los instalables
Se generarán dos carpetas, build
y dist_electron
. Dentro de la carpeta dist_electron
encontrarás el instalable del sistema operativo que creaste.
Paso 11: Vista de la aplicación de escritorio
Paso 12: Firma de la aplicación de escritorio
Es importante firmar la aplicación para asegurar su autenticidad y seguridad.
Nota: Es importante seguir investigando acerca de responsividad, funciones nativas y limitaciones.